// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;

namespace SharpCompress.Compressors.Deflate64;

internal static class FastEncoderStatics
{
    // static information for encoding, DO NOT MODIFY

    internal static ReadOnlySpan<byte> FAST_ENCODER_TREE_STRUCTURE_DATA =>
        new byte[]
        {
            0xec,
            0xbd,
            0x07,
            0x60,
            0x1c,
            0x49,
            0x96,
            0x25,
            0x26,
            0x2f,
            0x6d,
            0xca,
            0x7b,
            0x7f,
            0x4a,
            0xf5,
            0x4a,
            0xd7,
            0xe0,
            0x74,
            0xa1,
            0x08,
            0x80,
            0x60,
            0x13,
            0x24,
            0xd8,
            0x90,
            0x40,
            0x10,
            0xec,
            0xc1,
            0x88,
            0xcd,
            0xe6,
            0x92,
            0xec,
            0x1d,
            0x69,
            0x47,
            0x23,
            0x29,
            0xab,
            0x2a,
            0x81,
            0xca,
            0x65,
            0x56,
            0x65,
            0x5d,
            0x66,
            0x16,
            0x40,
            0xcc,
            0xed,
            0x9d,
            0xbc,
            0xf7,
            0xde,
            0x7b,
            0xef,
            0xbd,
            0xf7,
            0xde,
            0x7b,
            0xef,
            0xbd,
            0xf7,
            0xba,
            0x3b,
            0x9d,
            0x4e,
            0x27,
            0xf7,
            0xdf,
            0xff,
            0x3f,
            0x5c,
            0x66,
            0x64,
            0x01,
            0x6c,
            0xf6,
            0xce,
            0x4a,
            0xda,
            0xc9,
            0x9e,
            0x21,
            0x80,
            0xaa,
            0xc8,
            0x1f,
            0x3f,
            0x7e,
            0x7c,
            0x1f,
            0x3f,
        };

    internal static ReadOnlySpan<byte> B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA =>
        new byte[]
        {
            0xed,
            0xbd,
            0x07,
            0x60,
            0x1c,
            0x49,
            0x96,
            0x25,
            0x26,
            0x2f,
            0x6d,
            0xca,
            0x7b,
            0x7f,
            0x4a,
            0xf5,
            0x4a,
            0xd7,
            0xe0,
            0x74,
            0xa1,
            0x08,
            0x80,
            0x60,
            0x13,
            0x24,
            0xd8,
            0x90,
            0x40,
            0x10,
            0xec,
            0xc1,
            0x88,
            0xcd,
            0xe6,
            0x92,
            0xec,
            0x1d,
            0x69,
            0x47,
            0x23,
            0x29,
            0xab,
            0x2a,
            0x81,
            0xca,
            0x65,
            0x56,
            0x65,
            0x5d,
            0x66,
            0x16,
            0x40,
            0xcc,
            0xed,
            0x9d,
            0xbc,
            0xf7,
            0xde,
            0x7b,
            0xef,
            0xbd,
            0xf7,
            0xde,
            0x7b,
            0xef,
            0xbd,
            0xf7,
            0xba,
            0x3b,
            0x9d,
            0x4e,
            0x27,
            0xf7,
            0xdf,
            0xff,
            0x3f,
            0x5c,
            0x66,
            0x64,
            0x01,
            0x6c,
            0xf6,
            0xce,
            0x4a,
            0xda,
            0xc9,
            0x9e,
            0x21,
            0x80,
            0xaa,
            0xc8,
            0x1f,
            0x3f,
            0x7e,
            0x7c,
            0x1f,
            0x3f,
        };

    // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos
    //
    // Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch
    // length (not just each currentMatch length slot), complete with all the extra bits filled in, in
    // a single array element.
    //
    // There are many advantages to doing this:
    //
    // 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups
    //    on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength,
    //    g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask
    //
    // 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT
    //    code arrays in the other encoders (although they could be made into ULONGs with some
    //    modifications to the source).
    //
    // Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here.
    //
    // A completely different optimisation is used for the distance codes since, obviously, a table for
    // all 8192 distances combining their extra bits is not feasible.  The distance codeinfo table is
    // made up of code[], len[] and # extraBits for this code.
    //
    // The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better
    // cache locality, fewer memory operations.
    //

    // Encoding information for literal and Length.
    // The least 5 significant bits are the length
    // and the rest is the code bits.

    internal static readonly uint[] FAST_ENCODER_LITERAL_CODE_INFO =
    {
        0x0000d7ee,
        0x0004d7ee,
        0x0002d7ee,
        0x0006d7ee,
        0x0001d7ee,
        0x0005d7ee,
        0x0003d7ee,
        0x0007d7ee,
        0x000037ee,
        0x0000c7ec,
        0x00000126,
        0x000437ee,
        0x000237ee,
        0x000637ee,
        0x000137ee,
        0x000537ee,
        0x000337ee,
        0x000737ee,
        0x0000b7ee,
        0x0004b7ee,
        0x0002b7ee,
        0x0006b7ee,
        0x0001b7ee,
        0x0005b7ee,
        0x0003b7ee,
        0x0007b7ee,
        0x000077ee,
        0x000477ee,
        0x000277ee,
        0x000677ee,
        0x000017ed,
        0x000177ee,
        0x00000526,
        0x000577ee,
        0x000023ea,
        0x0001c7ec,
        0x000377ee,
        0x000777ee,
        0x000217ed,
        0x000063ea,
        0x00000b68,
        0x00000ee9,
        0x00005beb,
        0x000013ea,
        0x00000467,
        0x00001b68,
        0x00000c67,
        0x00002ee9,
        0x00000768,
        0x00001768,
        0x00000f68,
        0x00001ee9,
        0x00001f68,
        0x00003ee9,
        0x000053ea,
        0x000001e9,
        0x000000e8,
        0x000021e9,
        0x000011e9,
        0x000010e8,
        0x000031e9,
        0x000033ea,
        0x000008e8,
        0x0000f7ee,
        0x0004f7ee,
        0x000018e8,
        0x000009e9,
        0x000004e8,
        0x000029e9,
        0x000014e8,
        0x000019e9,
        0x000073ea,
        0x0000dbeb,
        0x00000ce8,
        0x00003beb,
        0x0002f7ee,
        0x000039e9,
        0x00000bea,
        0x000005e9,
        0x00004bea,
        0x000025e9,
        0x000027ec,
        0x000015e9,
        0x000035e9,
        0x00000de9,
        0x00002bea,
        0x000127ec,
        0x0000bbeb,
        0x0006f7ee,
        0x0001f7ee,
        0x0000a7ec,
        0x00007beb,
        0x0005f7ee,
        0x0000fbeb,
        0x0003f7ee,
        0x0007f7ee,
        0x00000fee,
        0x00000326,
        0x00000267,
        0x00000a67,
        0x00000667,
        0x00000726,
        0x00001ce8,
        0x000002e8,
        0x00000e67,
        0x000000a6,
        0x0001a7ec,
        0x00002de9,
        0x000004a6,
        0x00000167,
        0x00000967,
        0x000002a6,
        0x00000567,
        0x000117ed,
        0x000006a6,
        0x000001a6,
        0x000005a6,
        0x00000d67,
        0x000012e8,
        0x00000ae8,
        0x00001de9,
        0x00001ae8,
        0x000007eb,
        0x000317ed,
        0x000067ec,
        0x000097ed,
        0x000297ed,
        0x00040fee,
        0x00020fee,
        0x00060fee,
        0x00010fee,
        0x00050fee,
        0x00030fee,
        0x00070fee,
        0x00008fee,
        0x00048fee,
        0x00028fee,
        0x00068fee,
        0x00018fee,
        0x00058fee,
        0x00038fee,
        0x00078fee,
        0x00004fee,
        0x00044fee,
        0x00024fee,
        0x00064fee,
        0x00014fee,
        0x00054fee,
        0x00034fee,
        0x00074fee,
        0x0000cfee,
        0x0004cfee,
        0x0002cfee,
        0x0006cfee,
        0x0001cfee,
        0x0005cfee,
        0x0003cfee,
        0x0007cfee,
        0x00002fee,
        0x00042fee,
        0x00022fee,
        0x00062fee,
        0x00012fee,
        0x00052fee,
        0x00032fee,
        0x00072fee,
        0x0000afee,
        0x0004afee,
        0x0002afee,
        0x0006afee,
        0x0001afee,
        0x0005afee,
        0x0003afee,
        0x0007afee,
        0x00006fee,
        0x00046fee,
        0x00026fee,
        0x00066fee,
        0x00016fee,
        0x00056fee,
        0x00036fee,
        0x00076fee,
        0x0000efee,
        0x0004efee,
        0x0002efee,
        0x0006efee,
        0x0001efee,
        0x0005efee,
        0x0003efee,
        0x0007efee,
        0x00001fee,
        0x00041fee,
        0x00021fee,
        0x00061fee,
        0x00011fee,
        0x00051fee,
        0x00031fee,
        0x00071fee,
        0x00009fee,
        0x00049fee,
        0x00029fee,
        0x00069fee,
        0x00019fee,
        0x00059fee,
        0x00039fee,
        0x00079fee,
        0x00005fee,
        0x00045fee,
        0x00025fee,
        0x00065fee,
        0x00015fee,
        0x00055fee,
        0x00035fee,
        0x00075fee,
        0x0000dfee,
        0x0004dfee,
        0x0002dfee,
        0x0006dfee,
        0x0001dfee,
        0x0005dfee,
        0x0003dfee,
        0x0007dfee,
        0x00003fee,
        0x00043fee,
        0x00023fee,
        0x00063fee,
        0x00013fee,
        0x00053fee,
        0x00033fee,
        0x00073fee,
        0x0000bfee,
        0x0004bfee,
        0x0002bfee,
        0x0006bfee,
        0x0001bfee,
        0x0005bfee,
        0x0003bfee,
        0x0007bfee,
        0x00007fee,
        0x00047fee,
        0x00027fee,
        0x00067fee,
        0x00017fee,
        0x000197ed,
        0x000397ed,
        0x000057ed,
        0x00057fee,
        0x000257ed,
        0x00037fee,
        0x000157ed,
        0x00077fee,
        0x000357ed,
        0x0000ffee,
        0x0004ffee,
        0x0002ffee,
        0x0006ffee,
        0x0001ffee,
        0x00000084,
        0x00000003,
        0x00000184,
        0x00000044,
        0x00000144,
        0x000000c5,
        0x000002c5,
        0x000001c5,
        0x000003c6,
        0x000007c6,
        0x00000026,
        0x00000426,
        0x000003a7,
        0x00000ba7,
        0x000007a7,
        0x00000fa7,
        0x00000227,
        0x00000627,
        0x00000a27,
        0x00000e27,
        0x00000068,
        0x00000868,
        0x00001068,
        0x00001868,
        0x00000369,
        0x00001369,
        0x00002369,
        0x00003369,
        0x000006ea,
        0x000026ea,
        0x000046ea,
        0x000066ea,
        0x000016eb,
        0x000036eb,
        0x000056eb,
        0x000076eb,
        0x000096eb,
        0x0000b6eb,
        0x0000d6eb,
        0x0000f6eb,
        0x00003dec,
        0x00007dec,
        0x0000bdec,
        0x0000fdec,
        0x00013dec,
        0x00017dec,
        0x0001bdec,
        0x0001fdec,
        0x00006bed,
        0x0000ebed,
        0x00016bed,
        0x0001ebed,
        0x00026bed,
        0x0002ebed,
        0x00036bed,
        0x0003ebed,
        0x000003ec,
        0x000043ec,
        0x000083ec,
        0x0000c3ec,
        0x000103ec,
        0x000143ec,
        0x000183ec,
        0x0001c3ec,
        0x00001bee,
        0x00009bee,
        0x00011bee,
        0x00019bee,
        0x00021bee,
        0x00029bee,
        0x00031bee,
        0x00039bee,
        0x00041bee,
        0x00049bee,
        0x00051bee,
        0x00059bee,
        0x00061bee,
        0x00069bee,
        0x00071bee,
        0x00079bee,
        0x000167f0,
        0x000367f0,
        0x000567f0,
        0x000767f0,
        0x000967f0,
        0x000b67f0,
        0x000d67f0,
        0x000f67f0,
        0x001167f0,
        0x001367f0,
        0x001567f0,
        0x001767f0,
        0x001967f0,
        0x001b67f0,
        0x001d67f0,
        0x001f67f0,
        0x000087ef,
        0x000187ef,
        0x000287ef,
        0x000387ef,
        0x000487ef,
        0x000587ef,
        0x000687ef,
        0x000787ef,
        0x000887ef,
        0x000987ef,
        0x000a87ef,
        0x000b87ef,
        0x000c87ef,
        0x000d87ef,
        0x000e87ef,
        0x000f87ef,
        0x0000e7f0,
        0x0002e7f0,
        0x0004e7f0,
        0x0006e7f0,
        0x0008e7f0,
        0x000ae7f0,
        0x000ce7f0,
        0x000ee7f0,
        0x0010e7f0,
        0x0012e7f0,
        0x0014e7f0,
        0x0016e7f0,
        0x0018e7f0,
        0x001ae7f0,
        0x001ce7f0,
        0x001ee7f0,
        0x0005fff3,
        0x000dfff3,
        0x0015fff3,
        0x001dfff3,
        0x0025fff3,
        0x002dfff3,
        0x0035fff3,
        0x003dfff3,
        0x0045fff3,
        0x004dfff3,
        0x0055fff3,
        0x005dfff3,
        0x0065fff3,
        0x006dfff3,
        0x0075fff3,
        0x007dfff3,
        0x0085fff3,
        0x008dfff3,
        0x0095fff3,
        0x009dfff3,
        0x00a5fff3,
        0x00adfff3,
        0x00b5fff3,
        0x00bdfff3,
        0x00c5fff3,
        0x00cdfff3,
        0x00d5fff3,
        0x00ddfff3,
        0x00e5fff3,
        0x00edfff3,
        0x00f5fff3,
        0x00fdfff3,
        0x0003fff3,
        0x000bfff3,
        0x0013fff3,
        0x001bfff3,
        0x0023fff3,
        0x002bfff3,
        0x0033fff3,
        0x003bfff3,
        0x0043fff3,
        0x004bfff3,
        0x0053fff3,
        0x005bfff3,
        0x0063fff3,
        0x006bfff3,
        0x0073fff3,
        0x007bfff3,
        0x0083fff3,
        0x008bfff3,
        0x0093fff3,
        0x009bfff3,
        0x00a3fff3,
        0x00abfff3,
        0x00b3fff3,
        0x00bbfff3,
        0x00c3fff3,
        0x00cbfff3,
        0x00d3fff3,
        0x00dbfff3,
        0x00e3fff3,
        0x00ebfff3,
        0x00f3fff3,
        0x00fbfff3,
        0x0007fff3,
        0x000ffff3,
        0x0017fff3,
        0x001ffff3,
        0x0027fff3,
        0x002ffff3,
        0x0037fff3,
        0x003ffff3,
        0x0047fff3,
        0x004ffff3,
        0x0057fff3,
        0x005ffff3,
        0x0067fff3,
        0x006ffff3,
        0x0077fff3,
        0x007ffff3,
        0x0087fff3,
        0x008ffff3,
        0x0097fff3,
        0x009ffff3,
        0x00a7fff3,
        0x00affff3,
        0x00b7fff3,
        0x00bffff3,
        0x00c7fff3,
        0x00cffff3,
        0x00d7fff3,
        0x00dffff3,
        0x00e7fff3,
        0x00effff3,
        0x00f7fff3,
        0x00fffff3,
        0x0001e7f1,
        0x0003e7f1,
        0x0005e7f1,
        0x0007e7f1,
        0x0009e7f1,
        0x000be7f1,
        0x000de7f1,
        0x000fe7f1,
        0x0011e7f1,
        0x0013e7f1,
        0x0015e7f1,
        0x0017e7f1,
        0x0019e7f1,
        0x001be7f1,
        0x001de7f1,
        0x001fe7f1,
        0x0021e7f1,
        0x0023e7f1,
        0x0025e7f1,
        0x0027e7f1,
        0x0029e7f1,
        0x002be7f1,
        0x002de7f1,
        0x002fe7f1,
        0x0031e7f1,
        0x0033e7f1,
        0x0035e7f1,
        0x0037e7f1,
        0x0039e7f1,
        0x003be7f1,
        0x003de7f1,
        0x000047eb,
    };

    internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO =
    {
        0x00000f06,
        0x0001ff0a,
        0x0003ff0b,
        0x0007ff0b,
        0x0000ff19,
        0x00003f18,
        0x0000bf28,
        0x00007f28,
        0x00001f37,
        0x00005f37,
        0x00000d45,
        0x00002f46,
        0x00000054,
        0x00001d55,
        0x00000864,
        0x00000365,
        0x00000474,
        0x00001375,
        0x00000c84,
        0x00000284,
        0x00000a94,
        0x00000694,
        0x00000ea4,
        0x000001a4,
        0x000009b4,
        0x00000bb5,
        0x000005c4,
        0x00001bc5,
        0x000007d5,
        0x000017d5,
        0x00000000,
        0x00000100,
    };

    internal static readonly uint[] BIT_MASK =
    {
        0,
        1,
        3,
        7,
        15,
        31,
        63,
        127,
        255,
        511,
        1023,
        2047,
        4095,
        8191,
        16383,
        32767,
    };
    internal static readonly byte[] EXTRA_LENGTH_BITS =
    {
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        1,
        1,
        1,
        1,
        2,
        2,
        2,
        2,
        3,
        3,
        3,
        3,
        4,
        4,
        4,
        4,
        5,
        5,
        5,
        5,
        0,
    };
    internal static readonly byte[] EXTRA_DISTANCE_BITS =
    {
        0,
        0,
        0,
        0,
        1,
        1,
        2,
        2,
        3,
        3,
        4,
        4,
        5,
        5,
        6,
        6,
        7,
        7,
        8,
        8,
        9,
        9,
        10,
        10,
        11,
        11,
        12,
        12,
        13,
        13,
        0,
        0,
    };
    internal const int NUM_CHARS = 256;
    internal const int NUM_LENGTH_BASE_CODES = 29;
    internal const int NUM_DIST_BASE_CODES = 30;

    internal const uint FAST_ENCODER_POST_TREE_BIT_BUF = 0x0022;
    internal const int FAST_ENCODER_POST_TREE_BIT_COUNT = 9;

    internal const uint NO_COMPRESSION_HEADER = 0x0;
    internal const int NO_COMPRESSION_HEADER_BIT_COUNT = 3;
    internal const uint B_FINAL_NO_COMPRESSION_HEADER = 0x1;
    internal const int B_FINAL_NO_COMPRESSION_HEADER_BIT_COUNT = 3;
    internal const int MAX_CODE_LEN = 16;

    private static readonly byte[] S_DIST_LOOKUP = CreateDistanceLookup();

    private static byte[] CreateDistanceLookup()
    {
        var result = new byte[512];

        // Generate the global slot tables which allow us to convert a distance
        // (0..32K) to a distance slot (0..29)
        //
        // Distance table
        //   Extra           Extra               Extra
        // Code Bits Dist  Code Bits   Dist     Code Bits Distance
        // ---- ---- ----  ---- ----  ------    ---- ---- --------
        //   0   0    1     10   4     33-48    20    9   1025-1536
        //   1   0    2     11   4     49-64    21    9   1537-2048
        //   2   0    3     12   5     65-96    22   10   2049-3072
        //   3   0    4     13   5     97-128   23   10   3073-4096
        //   4   1   5,6    14   6    129-192   24   11   4097-6144
        //   5   1   7,8    15   6    193-256   25   11   6145-8192
        //   6   2   9-12   16   7    257-384   26   12  8193-12288
        //   7   2  13-16   17   7    385-512   27   12 12289-16384
        //   8   3  17-24   18   8    513-768   28   13 16385-24576
        //   9   3  25-32   19   8   769-1024   29   13 24577-32768

        // Initialize the mapping length (0..255) -> length code (0..28)
        //int length = 0;
        //for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) {
        //    for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++)
        //        lengthLookup[length++] = (byte) code;
        //}
        //lengthLookup[length-1] = (byte) code;

        // Initialize the mapping dist (0..32K) -> dist code (0..29)
        var dist = 0;
        int code;
        for (code = 0; code < 16; code++)
        {
            for (var n = 0; n < (1 << EXTRA_DISTANCE_BITS[code]); n++)
            {
                result[dist++] = (byte)code;
            }
        }

        dist >>= 7; // from now on, all distances are divided by 128

        for (; code < NUM_DIST_BASE_CODES; code++)
        {
            for (var n = 0; n < (1 << (EXTRA_DISTANCE_BITS[code] - 7)); n++)
            {
                result[256 + dist++] = (byte)code;
            }
        }

        return result;
    }

    // Return the position slot (0...29) of a match offset (0...32767)
    internal static int GetSlot(int pos) =>
        S_DIST_LOOKUP[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))];

    // Reverse 'length' of the bits in code
    public static uint BitReverse(uint code, int length)
    {
        uint newCode = 0;

        Debug.Assert(length > 0 && length <= 16, "Invalid len");
        do
        {
            newCode |= (code & 1);
            newCode <<= 1;
            code >>= 1;
        } while (--length > 0);

        return newCode >> 1;
    }
}
